#ifndef __EKOS_X86_LOCK_H__
#define __EKOS_X86_LOCK_H__

#include "x86.h"

/* spinlock */
typedef volatile uint32_t spin_lock_t;
static __inline void spl_init(spin_lock_t *lock) __attribute__((always_inline));
static __inline int  spl_try_acquire(spin_lock_t *lock) __attribute__((always_inline));
static __inline void spl_acquire(spin_lock_t *lock) __attribute__((always_inline));
static __inline void spl_release(spin_lock_t *lock) __attribute__((always_inline));

static __inline void
spl_init(spin_lock_t *lock)
{
	 *lock = 0;
}

static __inline int
spl_try_acquire(spin_lock_t *lock)
{
	 return xchg32(lock, 1) == 0;
}

static __inline void
spl_acquire(spin_lock_t *lock)
{
	 while (xchg32(lock, 1) == 1) ;
}

static __inline void
spl_release(spin_lock_t *lock)
{
	 *lock = 0;
}

/* scalable MCS lock */
struct mcs_lock_node_t
{
	 int free;
	 volatile struct mcs_lock_node_t * next;
};
typedef volatile struct mcs_lock_node_t * mcs_lock_state_t;
typedef volatile mcs_lock_state_t mcs_lock_t;

static __inline void mcs_init(mcs_lock_t *lock) __attribute__((always_inline));
static __inline int mcs_try_acquire(mcs_lock_t *lock, mcs_lock_state_t local) __attribute__((always_inline));
static __inline void mcs_acquire(mcs_lock_t *lock, mcs_lock_state_t local) __attribute__((always_inline));
static __inline void mcs_release(mcs_lock_t *lock, mcs_lock_state_t local) __attribute__((always_inline));

static __inline void
mcs_init(mcs_lock_t *lock)
{
	 *lock = NULL;
}

static __inline int
mcs_try_acquire(mcs_lock_t *lock, mcs_lock_state_t local)
{
	 return cmpxchg32(lock, 0, (uint32_t)local) == 0;
}

static __inline void
mcs_acquire(mcs_lock_t *lock, mcs_lock_state_t local)
{
	 local->free = 0;
	 local->next = NULL;
	 mcs_lock_state_t last = (mcs_lock_state_t)xchg32(lock, (uint32_t)local);
	 if (last != NULL)
	 {
		  last->next = local;
		  while (local->free != 0) ;
	 }
}

static __inline void
mcs_release(mcs_lock_t *lock, mcs_lock_state_t local)
{
	 if (local->next == NULL)
	 {
		  mcs_lock_state_t last = (mcs_lock_state_t)xchg32(lock, 0);
		  if (last == local) return;
		  mcs_lock_state_t nlast = (mcs_lock_state_t)xchg32(lock, (uint32_t)last);
		  mcs_lock_state_t next;
		  while (local->next == NULL) ;
		  if (nlast != NULL)
			   nlast->next = local->next;
		  else local->next->free = 1;
	 }
	 else local->next->free = 1;
}

#endif
